package org.qiuer.run;

import org.apache.commons.lang3.StringUtils;
import org.qiuer.ast.assign.AssignKind;
import org.qiuer.exception.Const;
import org.qiuer.exception.ERuntime;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * 1、声明变量支持const和let;
 * 2、取值逻辑，取值包括取函数，系统函数和自定义函数，还有系统变量；
 *
 */

/**
 * 可用于运行时的block上下文需要隔离的变量保存。
 */
public class Context{

  // 最初的作用域在最后。取值从最前作用域开始找。
  private LinkedList<Map<String, Variable>> variableScope = new LinkedList<>();
  private int indent = 0;
  private StringBuilder logBuilder = new StringBuilder();

  public Object updateVariable(String key, Object value) throws ERuntime {
    Variable variable = null;
    for (Map<String, Variable> env: variableScope){
      variable = env.get(key);
      if (variable != null){
        break;
      }
    }
    if(variable.isModifiable()){
      variable.setValue(value);
    }else
      throw new ERuntime(Const.EXCEPTION.VARIABLE_CANNOT_MODIFIED, "变量不可修改:" + key);
    return null;
  }

  public void declareVariable(String key, Object value, AssignKind kind) {
    boolean modifiable = true;
    if(kind == AssignKind.CONST){
      modifiable = false;
    }
    Variable variable = new Variable(key, value, modifiable);
    variableScope.getFirst().put(key, variable);
  }

  public Object getVariableValue(String key) throws ERuntime {
    Variable variable = null;
    for (Map<String, Variable> env: variableScope){
      variable = env.get(key);
      if (variable != null){
        return variable.getValue();
      }
    }
    throw new ERuntime(Const.EXCEPTION.UNDEFINED_VARIABLE, "未定义变量：" + key);
  }

  public Object getVariableValue(String key, Object defaultValue) {
    Variable variable = null;
    for (Map<String, Variable> env: variableScope){
      variable = env.get(key);
      if (variable != null){
        return variable.getValue();
      }
    }
    return defaultValue;
  }

  public void enterScope() {
    variableScope.addFirst(new HashMap<>());
    indent += 2;
  }

  public void exitScope() {
    variableScope.removeFirst();
    indent -=2;
  }

  public void println(String msg){
    logBuilder.append(StringUtils.repeat(" ", indent) + msg + "\n");
  }
  public void print(String msg){
    logBuilder.append(StringUtils.repeat(" ", indent) + msg);
  }

  public void exitLog(){
//    indent -= 2;
  }

  public void logToConsole(){
    System.out.println(logBuilder.toString());
  }

  public LinkedList<Map<String, Variable>> getVariableScope() {
    return variableScope;
  }

  public void setVariableScope(LinkedList<Map<String, Variable>> variableScope) {
    this.variableScope = variableScope;
  }
}
